/*******************************************************************************
 * Copyright (c) 2009-2011 Luaj.org. All rights reserved.
 * <p/>
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * <p/>
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * <p/>
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 ******************************************************************************/
package org.luaj.vm2.lib;

//TODO 与3.0不一样

import org.luaj.vm2.LuaError;
import org.luaj.vm2.LuaFunction;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;

import java.lang.reflect.Method;

/**
 * Subclass of {@link LuaFunction} common to Java functions exposed to lua.
 * <p/>
 * To provide for common implementations in JME and JSE,
 * library functions are typically grouped on one or more library classes
 * and an opcode per library function is defined and used to key the switch
 * to the correct function within the library.
 * <p/>
 * Since lua functions can be called with too few or too many arguments,
 * and there are overloaded {@link LuaValue#call()} functions with varying
 * number of arguments, a Java function exposed in lua needs to handle  the
 * argument fixup when a function is called with a number of arguments
 * differs from that expected.
 * <p/>
 * To simplify the creation of library functions,
 * there are 5 direct subclasses to handle common cases based on number of
 * argument values and number of return return values.
 * <ul>
 * <li>{@link ZeroArgFunction}</li>
 * <li>{@link OneArgFunction}</li>
 * <li>{@link TwoArgFunction}</li>
 * <li>{@link ThreeArgFunction}</li>
 * <li>{@link VarArgFunction}</li>
 * </ul>
 * <p/>
 * To be a Java library that can be loaded via {@code require}, it should have
 * a public constructor that returns a {@link LuaValue} that, when executed,
 * initializes the library.
 * <p/>
 * For example, the following code will implement a library called "hyperbolic"
 * with two functions, "sinh", and "cosh":
 * <pre> {@code
 * import org.luaj.vm2.LuaValue;
 * import org.luaj.vm2.lib.*;
 * <p/>
 * public class hyperbolic extends TwoArgFunction {
 * <p/>
 * 	public hyperbolic() {}
 * <p/>
 * 	public LuaValue call(LuaValue modname, LuaValue env) {
 * 		LuaValue library = tableOf();
 * 		library.set( "sinh", new sinh() );
 * 		library.set( "cosh", new cosh() );
 * 		env.set( "hyperbolic", library );
 * 		return library;
 * 	}
 * <p/>
 * 	static class sinh extends OneArgFunction {
 * 		public LuaValue call(LuaValue x) {
 * 			return LuaValue.valueOf(Math.sinh(x.checkdouble()));
 * 		}
 * 	}
 * <p/>
 * 	static class cosh extends OneArgFunction {
 * 		public LuaValue call(LuaValue x) {
 * 			return LuaValue.valueOf(Math.cosh(x.checkdouble()));
 * 		}
 * 	}
 * }
 * }</pre>
 * The default constructor is used to instantiate the library
 * in response to {@code require 'hyperbolic'} statement,
 * provided it is on Java&quot;s class path.
 * This instance is then invoked with 2 arguments: the name supplied to require(),
 * and the environment for this function.  The library may ignore these, or use
 * them to leave side effects in the global environment, for example.
 * In the previous example, two functions are created, 'sinh', and 'cosh', and placed
 * into a global table called 'hyperbolic' using the supplied 'env' argument.
 * <p/>
 * To test it, a script such as this can be used:
 * <pre> {@code
 * local t = require('hyperbolic')
 * print( 't', t )
 * print( 'hyperbolic', hyperbolic )
 * for k,v in pairs(t) do
 * 	print( 'k,v', k,v )
 * end
 * print( 'sinh(.5)', hyperbolic.sinh(.5) )
 * print( 'cosh(.5)', hyperbolic.cosh(.5) )
 * }</pre>
 * <p/>
 * It should produce something like:
 * <pre> {@code
 * t	table: 3dbbd23f
 * hyperbolic	table: 3dbbd23f
 * k,v	cosh	function: 3dbbd128
 * k,v	sinh	function: 3dbbd242
 * sinh(.5)	0.5210953
 * cosh(.5)	1.127626
 * }</pre>
 * <p/>
 * See the source code in any of the library functions
 * such as {@link BaseLib} or {@link TableLib} for other examples.
 */
abstract public class LibFunction extends LuaFunction {

    /**
     * User-defined opcode to differentiate between instances of the library function class.
     * <p/>
     * Subclass will typicall switch on this value to provide the specific behavior for each function.
     */
    public int opcode;

    //反射方法
    public Method method;

    /**
     * The common name for this function, useful for debugging.
     * <p/>
     * Binding functions initialize this to the name to which it is bound.
     */
    public String name;

    /**
     * Default constructor for use by subclasses
     */
    protected LibFunction() {
    }

    public String tojstring() {
        return name != null ? name : super.tojstring();
    }

    /**
     * Bind a set of library functions.
     * <p/>
     * An array of names is provided, and the first name is bound
     * with opcode = 0, second with 1, etc.
     *
     * @param env     The environment to apply to each bound function
     * @param factory the Class to instantiate for each bound function
     * @param names   array of String names, one for each function.
     * @see #bind(LuaValue, Class, String[], int)
     */
    protected void bind(LuaValue env, Class factory, String[] names) {
        bind(env, factory, names, 0);
    }

    /**
     * Bind a set of library functions, with an offset
     * <p/>
     * An array of names is provided, and the first name is bound
     * with opcode = {@code firstopcode}, second with {@code firstopcode+1}, etc.
     *
     * @param env         The environment to apply to each bound function
     * @param factory     the Class to instantiate for each bound function
     * @param names       array of String names, one for each function.
     * @param firstopcode the first opcode to use
     * @see #bind(LuaValue, Class, String[])
     */
    protected void bind(LuaValue env, Class factory, String[] names, int firstopcode) {
        try {
            for (int i = 0, n = names.length; i < n; i++) {
                LibFunction f = (LibFunction) factory.newInstance();
                f.opcode = firstopcode + i;
                f.name = names[i];
                env.set(f.name, f);
            }
        } catch (Exception e) {
            throw new LuaError("bind failed: " + e);
        }
    }

    /**
     * Java code generation utility to allocate storage for upvalue, leave it empty
     */
//    protected static LuaValue[] newupe() {
//        return new LuaValue[1]; remove by yanqiu
//    }

    /**
     * Java code generation utility to allocate storage for upvalue, initialize with nil
     */
//    protected static LuaValue[] newupn() {
//        return new LuaValue[]{NIL};  remove by yanqiu
//    }

    /**
     * Java code generation utility to allocate storage for upvalue, initialize with value
     */
//    protected static LuaValue[] newupl(LuaValue v) {
//        return new LuaValue[]{v}; remove by yanqiu
//    }

    public LuaValue call() {
        return argerror(1, "value");
    }

    public LuaValue call(LuaValue a) {
        return call();
    }

    public LuaValue call(LuaValue a, LuaValue b) {
        return call(a);
    }

    public LuaValue call(LuaValue a, LuaValue b, LuaValue c) {
        return call(a, b);
    }

    public LuaValue call(LuaValue a, LuaValue b, LuaValue c, LuaValue d) {
        return call(a, b, c);
    }

    public Varargs invoke(Varargs args) {
        switch (args.narg()) {
            case 0:
                return call();
            case 1:
                return call(args.arg1());
            case 2:
                return call(args.arg1(), args.arg(2));
            case 3:
                return call(args.arg1(), args.arg(2), args.arg(3));
            default:
                return call(args.arg1(), args.arg(2), args.arg(3), args.arg(4));
        }
    }
} 
